모노레포 버전 도입 재도전기
저번에 한 번 하려고 했다가 머리가 아퍼서 그냥 접고,
develop에 코드가 올라갈 때마다 관련 없는 프로젝트까지 배포 , 이 과정에서 준비되지 않은 코드가 (develop까지 올라갔으니 경우의 수는 무척 적지만) 배포 실패가 되는 게 말이 안된다고 생각해 다시 재도전
목표
-
프로젝트별 서로 다른 버전으로 관리-
이거 일단 버전 관리는 이번 목표에서 스킵. 여기서 버전 관리까지 하려니까 너무 꼬임.
-
changeset을 활용하는 목적은 "변경 사항 감지"에 그칠 것. 근데 이거 turborepo 자체로 확인하는 법 없나?
-
있다고 함;Í
-
turborepo는 현재 브랜치와 비교 대상(예: main 또는 이전 커밋)을 비교하여 영향을 받는 프로젝트만 필터링할 수 있음.
# develop 브랜치와 비교해서 변경한 것과, 그것에 의존하는 앱만 빌드 pnpm turbo build --filter=...[origin/develop]...[origin/develop]: "origin/develop 브랜치 이후로 변경된 모든 것과 그 변경사항에 의존하는 하위 프로젝트들"을 의미합니다.- 이 명령어를 실행하면, 변경되지 않은 앱은 자동으로
>>> FULL TURBO(캐시 히트) 가 뜨거나 실행 대상에서 제외됩니다.
-
그렇담 이제 changeset을 유지해야 할 이유가 없음. changeset은 업데이트를 해야 할 패키지를 명시적으로 선택할 수 있는 부분에서 낫지만, 버전 관리를 하지 않는다면 이 기능도 필요 없음.
-
-
-
운영/배포 서버 배포 용이하게 만들기
-
브랜치 전략
# main → project-a/b 모두 production 배포 # develop → project-a/b 모두 development 배포 # feature/* → 기능 개발 # hotfix/* → 긴급 수정 -
단순 push 말고, 버전 업데이트가 되면 배포할 수 있도록 -> stage 브랜치가 필요 없어짐.
-
-
change log가 쌓이도록 <- 변경 사항이 목록으로 보이도록
- 이 change log가 notion에 반영되면 참 좋을텐데 ...
1. 버전 관리 changeset
changeset 을 이용할거다.
목표
monorepo/
├── apps/
│ ├── project-a/
│ │ └── package.json # version: "2.3.1"
│ └── project-b/
│ └── package.json # version: "1.0.5"
├── packages/
│ ├── ui/
│ │ └── package.json # version: "2.5.3"
│ └── react-hooks/
│ └── package.json # version: "1.2.0"
시나리오 1. Feature 개발 및 develop 배포
develop 상태를 base로 할 때 기능 개발 완료 후 개발 서버에 배포한다는 가정.
순서 요약
- develop 기반의 feature 브랜치 생성 + 현재 버전 확인
- changeset 생성
--- Q. changeset 에서 major, minor를 내가 선택할 수 없음. 알아서 선택함.
- 변경사항 확인 및 커밋
--- Q. 이거 changeset이 먼저 커밋이 먼저 ?
--- Q. changeset 하나 당 한 기능이어야 하는건가?
-
push 및 PR 생성 (develop으로 향함)
-
워크플로우 확인
-
Feature 브랜치 생성 및 개발
# Feature 브랜치 생성
git checkout develop
git checkout -b feature/add-use-local-storage
# 수정
# 빌드 테스트
pnpm --filter @test/react-hooks build
- Changeset 생성
changeset 할 때 major/minor를 선택할 수 없을 때 확인해야 할 것
- .chanteset/config.json 설정 중
commit: false옵션 확인
# Changeset 생성
pnpm changeset
- 커밋 앤 푸시
changeset과 커밋 순서: changeset이 먼저? 커밋이 먼저?
- changeset 생성 -> 커밋해야 한다. changeset 한 뒤 생성되는 .md 문서도 코드 변경의 일부이기 때문에 함께 커밋되어야 함.
pnpm changesest 하는 기준: "배포 가능한 변경 단위"
changeset을 만들어야 하는 경우:
- 새로운 기능 추가
- 버그 수정
- breaking change
- 여러 패키지에 영향을 주는 변경
예시: 여러 작은 커밋, 하나의 changeset
# 1. Feature 브랜치 git checkout -b feature/add-debounce # 2. 첫 번째 커밋 (Changeset 없이) # ... useDebounce 기본 구현 ... git add . git commit -m "feat: implement basic useDebounce" git push # 3. 두 번째 커밋 (Changeset 없이) # ... 테스트 추가 ... git commit -m "test: add useDebounce tests" git push # 4. 세 번째 커밋 (문서 추가, Changeset 없이) # ... README 업데이트 ... git commit -m "docs: document useDebounce" git push # 5. 마지막에 Changeset 생성 pnpm changeset # @test/react-hooks: minor # "Added useDebounce hook with full test coverage" git add . git commit -m "chore: add changeset" git push # ✅ 여러 커밋 = 하나의 PR = 하나의 Changeset
# 변경사항 확인
git status
# 커밋
git add .
git commit -m "feat: add useLocalStorage hook"
# 푸시
git push -u origin feature/add-use-local-storage
-
PR 생성 및 머지
-
Github Actions 확인
-
PR 머지 결과:
✅ Changeset 감지
✅ 버전 업데이트 (예: 1.0.0 → 1.1.0)
✅ CHANGELOG.md 생성
✅ 버전 커밋 자동 생성
✅ Affected apps 감지 (project-a, project-b)
✅ 빌드 및 배포
-
-
결과 확인
# develop 브랜치 최신화
git checkout develop
git pull origin develop
# 버전 확인
cat packages/react-hooks/package.json | grep version
# 예상: "version": "1.1.0"
# CHANGELOG 확인
cat packages/react-hooks/CHANGELOG.md
# 커밋 히스토리 확인
git log --oneline -5
결과:
🔍 시나리오 1 완료 검증
================================
1️⃣ 현재 브랜치:
develop
2️⃣ 패키지 버전:
react-hooks: \Users\seungyeon\git\monorepo-test\packages\react-hooks\package.json
3️⃣ 파일 확인:
✅ useLocalStorage.ts 존재
✅ useLocalStorage.js (빌드) 존재
4️⃣ Changeset 상태:
✅ Changeset 처리 완료 (파일 삭제됨)
5️⃣ 최근 워크플로우:
conclusion name status
---------- ---- ------
success Deploy to Development completed
✅ 시나리오 1 완료!
================================
여기까지 했는데 오히려 더 복잡해지는 것 같아 그만함.
새롭게 짠 워크플로우 기반으로 진행
# ===== 시나리오 1: Project A 기능 추가 =====
git checkout -b feature/project-a-dashboard develop
# 1. 코드 수정
# apps/project-a/src/... 수정
# 2. Changeset 생성
pnpm changeset
# ? Which packages would you like to include?
# → @myapp/project-a 선택
# ? What kind of change is this for @myapp/project-a?
# → minor (새 기능)
# ? Summary: Added new dashboard widget
# 3. 생성된 changeset 파일
cat .changeset/strange-lions-jump.md
---
"@myapp/project-a": minor
---
Added new dashboard widget
# 4. Commit & Push
git add .
git commit -m "feat(project-a): add dashboard widget"
git push origin feature/project-a-dashboard
# 5. PR Merge 후 develop 브랜치에서
git checkout develop
git pull
# 6. 버전 업데이트 (develop에서 베타 버전)
pnpm changeset version --snapshot beta
# @myapp/project-a: 2.3.0 → 2.4.0-beta.0
git add .
git commit -m "chore: version packages (beta)"
git push
# 7. CI/CD가 자동으로 배포
# → Project A Development (v2.4.0-beta.0)
테스트 하다가 코드가 엉켜서 project-a만 테스트 해보려고 했는데, project-b까지 업데이트 됨.
보면 @test/project-a@0.0.0-beta-..., @test/project-b@0.0.0-beta..., @test/react-hooks@0.0.0-beta... 이렇게 업데이트 됨.
근데 왜 @test/react-hooks까지 업데이트 됐지? 난 얘 코드 변경한 적 없는데
changeset은 "어떤 패키지에서 코드가 변경되었는지"가 아니라, "현재 열려 있는 changeset 안에 어떤 패키지가 들어 있나"를 기준으로 버전을 올린다. 그래서 실제 코드 변경이 project-a, project-b에만 있어도, 이전에 만든 changeset에 @test/react-hooks가 포함되어 있었거나, 생성할 때 함께 체크했으면 올라감.
이전에 react-hooks를 변경한 changeset의 .md 파일이 같이 올라갔음.

버전 업데이트는 문제가 없고, actions의 워크플로우 보면 변경된 패키지만 개발 환경 배포됨.

시나리오 2. develop -> main 전체 배포
추가사항
이쯤돼서 궁금해지는 버전 vs. 릴리즈 vs. 태그
1. 버전
- 코드의 "상태"를 나타내는 식별자
- package.json 파일에 저장된 문자열
- semantic versioning 규칙 따름 (major, minor, patch)
특징
- 파일에 저장된 텍스트
- 커밋할 때마다 변경 가능
- 빌드된 앱에 포함됨
- git과 독립적 (git이 없어도 존재)
변경 시점 (feat. changeset)
pnpm changeset version
package.json의 version 필드가 변경됨.- 아직 git에는 커밋 안 됨
git add .
git commit -m "chore: bump version to "
[!tip] prod 배포 실수 방지하기
GitHub Environment 보호 규칙 쓰기 (가장 정석적인 방법)
GitHub 리포지토리 → Settings → Environments → New environment
이름: production
Required reviewers 를 1명 이상 지정
- 워크플로우 prod 잡에 environment 추가:
deploy-project-a-prod:
environment: production ...
이렇게 하면:
prod 잡이 실행되더라도 중간에 멈춰서 “승인 대기” 상태가 됩니다.
지정된 리뷰어가 GitHub Actions 화면에서 “Approve” 버튼을 눌러야만 계속 진행.
사람 승인 없이는 절대 prod 가 끝까지 가지 않으니,
실수 배포를 막는 가장 GitHub-공식적인 방법입니다.